using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using BoldBrick.Core.Logging;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using BoldBrick.SharePoint.CommonControls.Extensions;
using System.Linq;

namespace BoldBrick.SharePoint.CommonControls.DataObjects.Base
{
	/// <summary>
	/// Base metadata class for <see cref="SPList"/> object
	/// </summary>
	public abstract class BaseList : BaseDataObject
	{
		private static readonly ILogger log = Logger.GetLogger(typeof(BaseList));

		#region ------ Properties -----------------------------------------------------------------

		private List<ListColumn> columns;
		/// <summary>
		/// Columns of the list
		/// </summary>
		public List<ListColumn> Columns
		{
			get
			{
				if ( columns == null )
					InitializeColumns();
				
				return columns;
			}
		}

		private Dictionary<string, ListColumn> columnsByInternalName;
		/// <summary>
		/// Columns of the list by internal names
		/// </summary>
		public Dictionary<string, ListColumn> ColumnsByInternalName
		{
			get
			{
				if (columnsByInternalName == null )
					InitializeColumns();
				
				return columnsByInternalName;
			}
		}

		#endregion

		#region ------ Constructors ---------------------------------------------------------------

		/// <summary>
		/// Deafult constrcutor of <see cref="BaseList"/> class
		/// </summary>
		protected BaseList()
		{
			this.EnableAttachments = true;
			this.EnableFolderCreation = false;
			this.OnQuickLaunch = true;
		}

		#endregion

		
		#region ------ List Metadata --------------------------------------------------------------

		private int? listTemplateID;
		/// <summary>
		/// ID of template of the list
		/// </summary>
		public int ListTemplateID
		{
			get
			{
				if (!listTemplateID.HasValue)
				{
					listTemplateID = GetListTemplateID();
				}
				if (!listTemplateID.HasValue)
				{
					string errorMessage = string.Format("'ListTemplateID' in '{0}' class is not set.", this.GetType());
					throw new NullReferenceException(errorMessage);
				}
				return listTemplateID.Value;
			}
		}

		private SPListTemplateType? listTemplateType;
		/// <summary>
		/// Type of template of the list
		/// </summary>
		public SPListTemplateType? ListTemplateType
		{
			get
			{
				if (listTemplateType == null)
				{
					listTemplateType = GetListTemplateType();
				}
				if (listTemplateType == null)
				{
					string listTemplateNameTemp = GetListTemplateName();
					if (string.IsNullOrEmpty(listTemplateNameTemp))
					{
						string errorMessage = string.Format("'ListTemplateName' or 'ListTemplateType' in '{0}' class is not set.", this.GetType());
						throw new NullReferenceException(errorMessage);
					}

				}
				return listTemplateType;
			}
		}

		/// <summary>
		/// Gets/sets enabling of attachment in the list
		/// </summary>
		public bool EnableAttachments { get; set; }

		/// <summary>
		/// Gets/sets enabling of folder creation in the list
		/// </summary>
		public bool EnableFolderCreation { get; set; }

		/// <summary>
		/// Gets/sets showing the list on quick launch
		/// </summary>
		public bool OnQuickLaunch { get; set; }

		/// <summary>
		/// Gets list name binded to this data object dependent on culture
		/// </summary>
		/// <param name="language">Language of the list</param>
		/// <returns>Returns list name binded to this data object</returns>
		public virtual string GetListTitle(uint language)
		{
			string source = string.Format("$Resources: {0}", GetListTitleResourceKey());
			return SPUtility.GetLocalizedString(source, this.ResourceFileName, language);
		}

		/// <summary>
		/// Gets resource key for title of the list
		/// </summary>
		/// <returns>Returns resource key for title of the list</returns>
		protected virtual string GetListTitleResourceKey()
		{
			throw new NotImplementedException("Implement 'GetListTitleResourceKey' method or 'GetListTitle' method in inherited class.");
		}

		/// <summary>
		/// Gets description of the list
		/// </summary>
		/// <param name="language">Language identifier</param>
		/// <returns>Returns description of the list</returns>
		public virtual string GetListDescription(uint language)
		{
			string listDescriptionResourceKey = GetListDescriptionResourceKey();
			if (string.IsNullOrEmpty(listDescriptionResourceKey))
				return string.Empty;

			string source = string.Format("$Resources: {0}", listDescriptionResourceKey);
			return SPUtility.GetLocalizedString(source, this.ResourceFileName, language);
		}

		/// <summary>
		/// Gets resource key for descriptio of the list
		/// </summary>
		/// <returns>Returns resource key for descriptio of the list</returns>
		protected virtual string GetListDescriptionResourceKey()
		{
			return null;
		}

		/// <summary>
		/// Gets list name binded to this data object dependent on culture
		/// </summary>
		/// <param name="language">Language of the list</param>
		/// <returns>Returns list name binded to this data object</returns>
		public virtual string GetListName(uint language)
		{
			string source = string.Format("$Resources: {0}", GetListNameResourceKey());
			return SPUtility.GetLocalizedString(source, this.ResourceFileName, language);
		}

		/// <summary>
		/// Gets resource key for title of the list
		/// </summary>
		/// <returns>Returns resource key for title of the list</returns>
		protected virtual string GetListNameResourceKey()
		{
			throw new NotImplementedException("Implement 'GetListNameResourceKey' method or 'GetListName' method in inherited class.");
		}

		/// <summary>
		/// Gets list template name
		/// </summary>
		/// <returns>Returns list template name</returns>
		protected virtual string GetListTemplateName()
		{
			return null;
		}

		/// <summary>
		/// Gets list template type
		/// </summary>
		/// <returns>Returns list template type</returns>
		protected virtual SPListTemplateType? GetListTemplateType()
		{
			return null;
		}

		/// <summary>
		/// Gets ID of the list tamplate
		/// </summary>
		/// <returns>Returns ID of the list tamplate</returns>
		protected virtual int? GetListTemplateID()
		{
			return null;
		}

		#endregion


		#region ------ List Methods ---------------------------------------------------------------

		///// <summary>
		///// Gets instance of the list in the web
		///// </summary>
		///// <param name="web">Current web</param>
		///// <returns>Returns instance of the list in the web</returns>
		//public virtual SPList GetList(SPWeb web)
		//{
		//    if (web == null)
		//        return null;

		//    string listName = GetListTitle(web.Language);
		//    SPList list = web.Lists.TryGetList(listName);

		//    return list;
		//}
		
		/// <summary>
		/// Creates list in the web
		/// </summary>
		/// <param name="web">Web of the list</param>
		/// <returns>Returns created list</returns>
		public virtual SPList CreateList(SPWeb web)
		{
			string listName = GetListName(web.Language);
			string listTitle = GetListTitle(web.Language);
			string description = GetListDescription(web.Language);

			log.Write(LogLevel.Info, "Creating '{0}' list in '{1}' web.", listName, web.ServerRelativeUrl);

			SPList list = web.GetList(this);
			if (list != null)
			{
				string errorMessage = string.Format("'{0}' list already exist in '{1}' web.", listName, web.ServerRelativeUrl);
				log.Write(LogLevel.Error, errorMessage);
				throw new Exception(errorMessage);
			}

			string listTemplateName = GetListTemplateName();
			if (!string.IsNullOrEmpty(listTemplateName))
			{
				log.Write(LogLevel.Info, "Creating '{0}' list based on '{1}' template in '{2}' web.", listName, listTemplateName, web.ServerRelativeUrl);
				list = web.CreateList(listTemplateName, listTitle, listName, description);
			}
			else if (this.ListTemplateType.HasValue)
			{
				log.Write(LogLevel.Info, "Creating '{0}' list based on '{1}' template in '{2}' web.", listName, this.ListTemplateType, web.ServerRelativeUrl);
				list = web.CreateList(this.ListTemplateType.Value, listTitle, listName, description);
			}

			if (list != null)
			{
				web.SetListIDProperty(this, list.ID);
				UpdateList(web, list);
				list.Update();
			}
			else
			{
				log.Write(LogLevel.Error, "Unable to create '{0}' list in '{1}' web. A template name nor a template type is not set.", listName, web.ServerRelativeUrl);
			}

			return list;
		}

		/// <summary>
		/// Updates the list properties
		/// </summary>
		/// <param name="list">The list</param>
		/// <param name="web">Parent web of the list</param>
		public void UpdateList(SPWeb web, SPList list)
		{
			OnBeforeListUpdated(web, list);
			list.Title = GetListTitle(web.Language);
			list.Description = GetListDescription(web.Language);
			list.EnableAttachments = this.EnableAttachments;
			list.OnQuickLaunch = this.OnQuickLaunch;
			list.EnableFolderCreation = this.EnableFolderCreation;
			OnAfterListUpdated(web, list);
		}

		/// <summary>
		/// Method is fired before <see cref="UpdateColumnOrder"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnBeforeListUpdated(SPWeb web, SPList list)
		{
			
		}

		/// <summary>
		/// Method is fired after <see cref="UpdateColumnOrder"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnAfterListUpdated(SPWeb web, SPList list)
		{
			
		}

		#endregion

		#region ------ Column Order Update --------------------------------------------------------

		/// <summary>
		/// Updates columns order in the list
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="spList">The list</param>
		public void UpdateColumnOrder(SPWeb web, SPList spList)
		{
			OnBeforeColumnOrderUpdated(web, spList);
			string listUrl = spList.RootFolder.ServerRelativeUrl;
			log.Write(LogLevel.Info, "Update column order in '{0}' list.", listUrl);

			List<ListColumn> orderedColumns = GetOrderedListColumns();
			if (spList.ContentTypes.Count == 0)
			{
				log.Write(LogLevel.Warn, "Update column order cannot be finished. '{0}' list has no content type.", listUrl);
			}
			else
			{
				SPContentType defaultContentType = spList.ContentTypes[0];
				log.Write(LogLevel.Info, "Update column order in '{0}' list in '{1}' content type", listUrl, defaultContentType.Name);

				string[] fieldInternalNames = orderedColumns
					.Select(column => column.InternalName)
					.ToArray();

				defaultContentType.FieldLinks.Reorder(fieldInternalNames);
				defaultContentType.Update();
				log.Write(LogLevel.Info, "Update column order in '{0}' list in '{1}' content type has successfully finished.", listUrl, defaultContentType.Name);
			}
			OnAfterColumnOrderUpdated(web, spList);
		}

		/// <summary>
		/// Method is fired before <see cref="UpdateColumnOrder"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnBeforeColumnOrderUpdated(SPWeb web, SPList list)
		{

		}

		/// <summary>
		/// Method is fired after <see cref="UpdateColumnOrder"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnAfterColumnOrderUpdated(SPWeb web, SPList list)
		{

		}

		#endregion

		#region ------ Columns Update -------------------------------------------------------------

		/// <summary>
		/// Updates columns of the list (adding and updating properties)
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="spList">The list</param>
		public void UpdateColumns(SPWeb web, SPList spList)
		{
			OnBeforeColumnsUpdated(web, spList);
			string listUrl = spList.RootFolder.ServerRelativeUrl;
			foreach (ListColumn listColumn in this.Columns)
			{
				if (listColumn == null)
					continue;

				if (listColumn.InternalName == "Attachments" || listColumn.InternalName == "DocIcon")
					continue;
				
				web.AllowUnsafeUpdates = true;
				
				log.Write(LogLevel.Info, "Update '{0}' column in '{1}' list.", listColumn.InternalName, listUrl);
				SPField field = AddFieldToList(web, spList, listColumn);
				if ( field == null )
					continue;
				
				field.SetFieldProperties(listColumn, web.Language);
				field.Update(true);
			}
			OnAfterColumnsUpdated(web, spList);
		}

		/// <summary>
		/// Method is fired before <see cref="UpdateColumns"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnBeforeColumnsUpdated(SPWeb web, SPList list)
		{
			
		}
		
		/// <summary>
		/// Method is fired before <see cref="UpdateColumns"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnAfterColumnsUpdated(SPWeb web, SPList list)
		{
			
		}

		#endregion

		#region ------ Common Columns Methods -----------------------------------------------------

		/// <summary>
		/// Adds field to the list
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="spList">The list</param>
		/// <param name="listColumn">Metadata class of the field to add</param>
		/// <returns>Returns created <see cref="SPField"/> object of the list</returns>
		protected static SPField AddFieldToList(SPWeb web, SPList spList, ListColumn listColumn)
		{
			string listUrl = spList.RootFolder.ServerRelativeUrl;
			string columnName = listColumn.InternalName;
			
			SPField field = spList.Fields.GetFieldByInternalName(columnName);
			if ( field != null )
				return field;

			SPField fieldInWeb = web.AvailableFields.GetFieldByInternalName(columnName);
			
			if (fieldInWeb == null)
			{
				log.Write(LogLevel.Error, "'{0}' field has not been found in '{1}' web.", listColumn.InternalName, web.ServerRelativeUrl);
				return null;
			}

			try
			{
				spList.Fields.Add(fieldInWeb);
			}
			catch ( Exception ex )
			{
				string message = string.Format("'{0}' field has not been added into '{1}' list",
					listColumn.InternalName,
					spList.Title);
				log.Write(LogLevel.Error, ex, "'{0}' field cannot be added to '{1}' list.", listColumn.InternalName, listUrl);
				throw new InvalidOperationException(message, ex);
			}
			
			field = spList.Fields.GetFieldByInternalName(columnName);
			try
			{
				field.Title = listColumn.GetDisplayName(web.Language);
			}
			catch (Exception ex)
			{
				log.Write(LogLevel.Error, ex, "Unable to update '{0}' field in '{1}' list.", listColumn.InternalName, listUrl);
				field.Delete();
				throw;
			}

			return field;
		}

		private void AddColumn(ListColumn listColumn)
		{
			if ( this.Columns.Contains(listColumn) )
				return;
			
			this.Columns.Add(listColumn);
			this.ColumnsByInternalName[listColumn.InternalName] = listColumn;
		}
		
		/// <summary>
		/// Initialize columns lists of this class
		/// </summary>
		protected void InitializeColumns()
		{
			if ( columnsByInternalName == null )
				columnsByInternalName = new Dictionary<string, ListColumn>();
			if ( columns == null )
				columns = new List<ListColumn>();
			
			Type listType = this.GetType();
			PropertyInfo[] properties = listType.GetProperties();

			foreach (PropertyInfo property in properties)
			{
				ListColumn listColumn;
				
				try
				{
					listColumn = property.GetValue(this, null) as ListColumn;
				}
				catch
				{
					listColumn = null;
				}
				
				if (listColumn != null)
					AddColumn(listColumn);
			}
		}

		

		/// <summary>
		/// Gets list of ordered columns of the list
		/// </summary>
		/// <returns>Returns list of ordered columns of the list</returns>
		public virtual List<ListColumn> GetOrderedListColumns()
		{
			return this.Columns;
		}

		
		#endregion

		#region ------ Content Types Methods ------------------------------------------------------

		/// <summary>
		/// Gets list of content types of the list
		/// </summary>
		/// <returns>Returns Gets list of content types of the list</returns>
		public virtual List<BaseContentType> GetContentTypes()
		{
			return new List<BaseContentType>();
		}

		/// <summary>
		/// Updates content types of the list (adding content types)
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list">List to update</param>
		public void UpdateContentTypes(SPWeb web, SPList list)
		{
			OnBeforeContentTypesUpdated(web, list);
			log.Write(LogLevel.Info, "Update content types in '{0}' list in '{1}' web.", this.GetListName(web.Language), web.ServerRelativeUrl);
			List<BaseContentType> contentTypes = GetContentTypes();
			if (contentTypes == null || contentTypes.Count == 0)
			{
				log.Write(LogLevel.Info, "No content type for update in '{0}' list in '{1}' web.", this.GetListName(web.Language), web.ServerRelativeUrl);
			}
			else
			{
				list.ContentTypesEnabled = true;

				List<SPContentTypeId> currentListContentTypeIds = list.ContentTypes
					.OfType<SPContentType>()
					.Select(contentType => contentType.Id)
					.ToList();

				foreach (BaseContentType contentType in contentTypes)
				{
					// check if content type is already in the list
					SPContentTypeId listContentTypeId = list.ContentTypes.BestMatch(contentType.ID);
					if (listContentTypeId.IsChildOf(contentType.ID))
					{
						log.Write(LogLevel.Info, "'{0}' content type is already added in '{1}' list in '{2}' web.",
							contentType.GetName(web.Language),
							this.GetListName(web.Language),
							web.ServerRelativeUrl);
						currentListContentTypeIds.Remove(listContentTypeId);
						continue;
					}

					// get content type from web
					SPContentTypeId webContentTypeId = web.AvailableContentTypes.BestMatch(contentType.ID);
					if (!webContentTypeId.Equals(contentType.ID))
					{
						log.Write(LogLevel.Info, "Update '{0}' content type in '{1}' list in '{2}' web failed. Content type not found in parent web.",
							contentType.GetName(web.Language),
							this.GetListName(web.Language),
							web.ServerRelativeUrl);
						continue;
					}
					// add content type to the list
					SPContentType webContentType = web.AvailableContentTypes[webContentTypeId];
					list.ContentTypes.Add(webContentType);
				}

				// delete content types from list
				foreach (SPContentTypeId contentTypeId in currentListContentTypeIds)
				{
					log.Write(LogLevel.Info, "Delete '{0}' content type from '{1}' list in '{2}' web.",
						contentTypeId,
						this.GetListName(web.Language),
						web.ServerRelativeUrl);
					list.ContentTypes.Delete(contentTypeId);
				}
			}

			OnAfterContentTypesUpdated(web, list);
		}

		/// <summary>
		/// Method is fired before <see cref="UpdateContentTypes"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnBeforeContentTypesUpdated(SPWeb web, SPList list)
		{
			
		}

		/// <summary>
		/// Method is fired after <see cref="UpdateContentTypes"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnAfterContentTypesUpdated(SPWeb web, SPList list)
		{
			
		}

		#endregion

		#region ------ View Methods ---------------------------------------------------------------

		/// <summary>
		/// Updates all views of the list
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list">The list</param>
		public void UpdateViews(SPWeb web, SPList list)
		{
			OnBeforeViewsUpdated(web, list);
			log.Write(LogLevel.Info, "Update views of '{0}' list in '{1}' web.", this.GetListName(web.Language), web.ServerRelativeUrl);

			ViewsCollection viewsToUpdate = GetViews();
			if (viewsToUpdate == null || viewsToUpdate.Count == 0)
			{
				log.Write(LogLevel.Warn, "Update views of '{0}' list in '{1}' web failed. No view found.", this.GetListName(web.Language), web.ServerRelativeUrl);
			}
			else
			{
				foreach (BaseView view in viewsToUpdate)
				{
					UpdateView(web, list, view);
				}
			}
			
			OnAfterViewsUpdated(web, list);
		}

		/// <summary>
		/// Method is fired before <see cref="UpdateViews"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnBeforeViewsUpdated(SPWeb web, SPList list)
		{
			
		}

		/// <summary>
		/// Method is fired after <see cref="UpdateViews"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnAfterViewsUpdated(SPWeb web, SPList list)
		{
			
		}

		/// <summary>
		/// Updates view according to its metadata object
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list">Parent list of the view</param>
		/// <param name="view">Metadata object of the view</param>
		public void UpdateView(SPWeb web, SPList list, BaseView view)
		{
			OnBeforeViewUpdated(web, list, view);
			log.Write(LogLevel.Info, "Update default view of '{0}' list in '{1}' web.", this.GetListName(web.Language), web.ServerRelativeUrl);

			string viewTitle = view.GetTitle(web.Language);
			string viewPageName = view.GetPageName(web.Language);

			// get view solumns
			List<ListColumn> viewColumns = view.GetViewColumns();
			if (viewColumns == null || viewColumns.Count == 0)
			{
				log.Write(LogLevel.Error, "'{0}' view of '{1}' list in '{2}' web is not defined. No changes done.",
					viewTitle, this.GetListName(web.Language), web.ServerRelativeUrl);
			}
			else
			{
				// get the view

				string pageUrl = string.Format("/{0}.aspx", viewPageName).ToLower();
				SPView spView = list.Views.OfType<SPView>()
					.FirstOrDefault(v => v.Url.ToLower().EndsWith(pageUrl));

				// check if all fields are in the list
				List<string> viewColumnsFields = new List<string>();
				foreach (ListColumn viewColumn in viewColumns)
				{
					SPField field = list.Fields.TryGetFieldByStaticName(viewColumn.InternalName);
					if (field != null)
						viewColumnsFields.Add(viewColumn.InternalName);
				}

				// serialize fields to array for view
				string[] viewFields = viewColumnsFields.ToArray();
				string viewFieldsLog = viewFields.Aggregate((l, r) => string.Format("{0};{1}", l, r));
				log.Write(LogLevel.Info, "Update '{0}' view of '{0}' list in '{1}' web. Set fields: {2}", this.GetListName(web.Language), web.ServerRelativeUrl, viewFieldsLog);

				string query = view.Query;
				log.Write(LogLevel.Info, "Update '{0}' view of '{0}' list in '{1}' web. Set query: {2}", this.GetListName(web.Language), web.ServerRelativeUrl, query);

				// view does not exist, create it
				if (spView == null)
				{
					log.Write(LogLevel.Info, "'{0}' view of '{1}' list in '{2}' web is not created. Creating view.",
						viewTitle, this.GetListName(web.Language), web.ServerRelativeUrl);
					StringCollection strCollViewFields = new StringCollection();
					strCollViewFields.AddRange(viewFields);
					spView = list.Views.Add(viewPageName, strCollViewFields, query, view.RowLimit, view.Paged, view.IsDefault);
				}
				else
				{
					spView.ViewFields.DeleteAll();
					foreach (string viewField in viewFields)
					{
						spView.ViewFields.Add(viewField);
					}

					// setup properties
					spView.Paged = view.Paged;
					spView.RowLimit = view.RowLimit;
					spView.Query = query;
					spView.DefaultView = view.IsDefault;
					
					
				}

				if (!string.IsNullOrEmpty(view.XslLink))
				{
					spView.XslLink = view.XslLink;
				}

				spView.Title = viewTitle;
				spView.Update();

				// setup content type id
				if (view.ContentTypeId.HasValue)
				{
					SPContentTypeId contentTypeIdToSet = view.ContentTypeId.Value;
					SPContentType childContentType = list.ContentTypes.GetChildContentType(contentTypeIdToSet);
					if (childContentType != null)
						contentTypeIdToSet = childContentType.Id;

					spView.ContentTypeId = contentTypeIdToSet;
					spView.DefaultViewForContentType = view.DefaultViewForContentType;
					spView.Update();
				}
			}

			OnAfterViewUpdated(web, list, view);
		}

		/// <summary>
		/// Method is fired after <see cref="UpdateView"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		/// <param name="view"><see cref="BaseView"/> object to update</param>
		protected virtual void OnBeforeViewUpdated(SPWeb web, SPList list, BaseView view)
		{
			
		}

		/// <summary>
		/// Method is fired after <see cref="UpdateView"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		/// <param name="view"><see cref="BaseView"/> object to update</param>
		protected virtual void OnAfterViewUpdated(SPWeb web, SPList list, BaseView view)
		{
			
		}

		/// <summary>
		/// Gets collections of views of this list without default view
		/// </summary>
		/// <returns>Returns collections of views of this list without default view</returns>
		public virtual ViewsCollection GetViews()
		{
			return new ViewsCollection();
		}

		#endregion

		#region ------ Event Receivers ------------------------------------------------------------

		/// <summary>
		/// Adds all event receivers of this metadata list object to <see cref="SPList"/> object
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object</param>
		public void AddAllEventReceivers(SPWeb web, SPList list)
		{
			OnBeforeAllEventsReceiversAdded(web, list);
			List<BaseEventReceiverObject> receivers = GetEventReceivers();
			foreach (BaseEventReceiverObject receiver in receivers)
			{
				list.AddEventReceiver(receiver);
			}
			OnAfterAllEventsReceiversAdded(web, list);
		}

		/// <summary>
		/// Method is fired before <see cref="AddAllEventReceivers"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnBeforeAllEventsReceiversAdded(SPWeb web, SPList list)
		{
			
		}

		/// <summary>
		/// Method is fired after <see cref="AddAllEventReceivers"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnAfterAllEventsReceiversAdded(SPWeb web, SPList list)
		{
			
		}

		/// <summary>
		/// Removes all event receivers of this metadata list object from <see cref="SPList"/> object
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object</param>
		public void RemoveAllEventReceivers(SPWeb web, SPList list)
		{
			OnBeforeAllEventsReceiversRemoved(web, list);
			List<BaseEventReceiverObject> receivers = GetEventReceivers();
			foreach (BaseEventReceiverObject receiver in receivers)
			{
				list.RemoveEventReceiver(receiver);
			}
			OnAfterAllEventsReceiversRemoved(web, list);
		}

		/// <summary>
		/// Method is fired before <see cref="RemoveAllEventReceivers"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnBeforeAllEventsReceiversRemoved(SPWeb web, SPList list)
		{
			
		}

		/// <summary>
		/// Method is fired after <see cref="RemoveAllEventReceivers"/> method is fired
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="list"><see cref="SPList"/> object to update</param>
		protected virtual void OnAfterAllEventsReceiversRemoved(SPWeb web, SPList list)
		{
			
		}

		/// <summary>
		/// Gets all event receivers of this metadata list object
		/// </summary>
		/// <returns>Returns all event receivers of this metadata list object</returns>
		public virtual List<BaseEventReceiverObject> GetEventReceivers()
		{
			return new List<BaseEventReceiverObject>();
		}

		#endregion


		#region ------ Workflows ------------------------------------------------------------------

		/// <summary>
		/// Gets all associated workflows to the list
		/// </summary>
		/// <returns>Returns all associated workflows to the list</returns>
		public virtual List<Workflow> GetWorkflows()
		{
			return new List<Workflow>();
		}

		/// <summary>
		/// Associates all workflow to the list
		/// </summary>
		/// <param name="web">Parent web of the list</param>
		/// <param name="spList">The list</param>
		public void AssociatedWorkflows(SPWeb web, SPList spList)
		{
			List<Workflow> workflows = GetWorkflows();
			if (workflows == null)
			{
				log.Write(LogLevel.Warn, "No workflow is associated. '{0}' list in '{1}'.", this.GetListName(web.Language), web.ServerRelativeUrl);
				return;
			}

			foreach (Workflow workflow in workflows)
			{
				spList.AssociateWorkflow(web, workflow);
			}
		}

		#endregion
	}
	
}